﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// UI层级枚举
/// </summary>
public enum E_UILayer
{
    Bottom , 
    Middle , 
    Top , 
    System
}

///<summary>
///UI管理器
///1. 管理所有显示的面板
///2. 提供给外部 显示或者隐藏的接口
///</summary>
public class UIManager : BaseManager<UIManager>
{
    //下边这个字典保存的是全部目前显示的面板 , 你要是不显示就要被移出这个字典
    public Dictionary<string, BasePanel> panelDic = new Dictionary<string, BasePanel>();

    private Transform Bottom;
    private Transform Middle;
    private Transform Top;
    private Transform System;
    //把canvas设成public方便外部调用
    public RectTransform canvas;
    public UIManager()
    {
        //在构造函数创建canvas预制体
        //GameObject obj=Resources.Load<GameObject>("UI/Canvas");
        GameObject obj = ResManager.GetInstance().Load<GameObject>("UI/Canvas");
        canvas = obj.transform as RectTransform;
        GameObject.DontDestroyOnLoad(obj);

        //找到各层
        Bottom = canvas.Find("Bottom");
        Middle = canvas.Find("Middle");
        Top = canvas.Find("Top");
        System = canvas.Find("System");

        //创建eventSystem , 并让它在过场景的时候不被移除
        obj = ResManager.GetInstance().Load<GameObject>("UI/EventSystem");
        GameObject.DontDestroyOnLoad(obj);
    }
    /// <summary>
    /// 显示面板方法
    /// </summary>
    /// <typeparam name="T">面板脚本类型</typeparam>
    /// <param name="PanelName">面板名</param>
    /// <param name="layer">面板显示在哪一层</param>
    /// <param name="callback">面板创建完成后执行的函数</param>
    public void ShowPanel<T>(string PanelName , E_UILayer layer = E_UILayer.Middle , UnityAction<T>callback = null) where T:BasePanel
    { 
        //如果界面已经被显示了 , 就不需要执行资源加载和显示了 , 直接调用后边的需要执行的委托 , 顺便执行完委托要结束这个函数(return)
        if(panelDic.ContainsKey(PanelName))
        {
            panelDic[PanelName].Show();//这里加了个方法 , 就basepanel定义的 , 子类可以重写
            if (callback != null)
            {
                callback(panelDic[PanelName] as T);
                
            }
            return;
        }
        //加载传入的Panel对象
        ResManager.GetInstance().LoadAsync<GameObject> ("UI/" + PanelName , (obj)=>
        {
            //把加载出来的Panel放到Canvas的某个层下边
            //默认放在底层
            Transform father = Bottom;
            switch (layer)
            {
                case E_UILayer.Middle:
                    father = Middle;
                    break;
                case E_UILayer.Top:
                    father = Top;
                    break;
                case E_UILayer.System:
                    father = System;
                    break;
            }
            obj.transform.SetParent(father);
            obj.transform.localPosition = Vector3.zero;
            obj.transform.localScale = Vector3.one;
            (obj.transform as RectTransform).offsetMax = Vector3.zero;
            (obj.transform as RectTransform).offsetMin = Vector3.zero;
            //获取面板的脚本
            T panel = obj.GetComponent<T>();
            //下边其实就是说 , 如果传入的参数里有第三个参数的话 , 那我就执行下边这段代码 , 第三个参数是个委托 , 就是因为这个showpanel是用异步加载实现的 , 所以不能确定什么时候加载完 , 
            //想要在加载完成后执行某个函数的话就必须用这样的方法 , 先在子类定义一个方法 , 然后把方法的委托传进来 , 下边代码检测到callback不为空就会执行 , 顺便这个函数必须是无返回值有参数的 , 看样子参数还必须是panel脚本
           //简单的说:面板完成后执行下边的方法 , 方法由外部传入
            if(callback!=null)
            {
                callback(panel);
            }
            //字典里存每个界面的脚本 , 不要存他们的物体对象 , 到时候要删除物体对象的话就通过脚本.gameObject的方式删 
            panelDic.Add(PanelName, panel);
        });


    }
    /// <summary>
    /// 这是上边你那个函数的重载 , 提供了不传递泛型参数的UnityAction版本
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="PanelName"></param>
    /// <param name="layer"></param>
    /// <param name="callback"></param>
    public void ShowPanel<T>(string PanelName, E_UILayer layer = E_UILayer.Middle, UnityAction callback = null) where T : BasePanel
    {
        //如果界面已经被显示了 , 就不需要执行资源加载和显示了 , 直接调用后边的需要执行的委托 , 顺便执行完委托要结束这个函数(return)
        if (panelDic.ContainsKey(PanelName))
        {
            panelDic[PanelName].Show();//这里加了个方法 , 就basepanel定义的 , 子类可以重写
            if (callback != null)
            {
                callback();

            }
            return;
        }
        //加载传入的Panel对象
        ResManager.GetInstance().LoadAsync<GameObject>("UI/" + PanelName, (obj) =>
        {
            //把加载出来的Panel放到Canvas的某个层下边
            //默认放在底层
            Transform father = Bottom;
            switch (layer)
            {
                case E_UILayer.Middle:
                    father = Middle;
                    break;
                case E_UILayer.Top:
                    father = Top;
                    break;
                case E_UILayer.System:
                    father = System;
                    break;
            }
            obj.transform.SetParent(father);
            obj.transform.localPosition = Vector3.zero;
            obj.transform.localScale = Vector3.one;
            (obj.transform as RectTransform).offsetMax = Vector3.zero;
            (obj.transform as RectTransform).offsetMin = Vector3.zero;
            //获取面板的脚本
            T panel = obj.GetComponent<T>();
            //下边其实就是说 , 如果传入的参数里有第三个参数的话 , 那我就执行下边这段代码 , 第三个参数是个委托 , 就是因为这个showpanel是用异步加载实现的 , 所以不能确定什么时候加载完 , 
            //想要在加载完成后执行某个函数的话就必须用这样的方法 , 先在子类定义一个方法 , 然后把方法的委托传进来 , 下边代码检测到callback不为空就会执行 , 顺便这个函数必须是无返回值有参数的 , 看样子参数还必须是panel脚本
            //简单的说:面板完成后执行下边的方法 , 方法由外部传入
            if (callback != null)
            {
                callback();
            }
            //字典里存每个界面的脚本 , 不要存他们的物体对象 , 到时候要删除物体对象的话就通过脚本.gameObject的方式删 
            panelDic.Add(PanelName, panel);
        });


    }
    /// <summary>
    /// 这还是上边函数的重载 , 提供了只显示 , 不传递委托的方法
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="PanelName"></param>
    /// <param name="layer"></param>
    public void ShowPanel<T>(string PanelName, E_UILayer layer = E_UILayer.Middle) where T : BasePanel
    {
        //如果界面已经被显示了 , 就不需要执行资源加载和显示了 , 直接调用后边的需要执行的委托 , 顺便执行完委托要结束这个函数(return)
        if (panelDic.ContainsKey(PanelName))
        {
            panelDic[PanelName].Show();//这里加了个方法 , 就basepanel定义的 , 子类可以重写
           
            return;
        }
        //加载传入的Panel对象
        ResManager.GetInstance().LoadAsync<GameObject>("UI/" + PanelName, (obj) =>
        {
            //把加载出来的Panel放到Canvas的某个层下边
            //默认放在底层
            Transform father = Bottom;
            switch (layer)
            {
                case E_UILayer.Middle:
                    father = Middle;
                    break;
                case E_UILayer.Top:
                    father = Top;
                    break;
                case E_UILayer.System:
                    father = System;
                    break;
            }
            obj.transform.SetParent(father);
            obj.transform.localPosition = Vector3.zero;
            obj.transform.localScale = Vector3.one;
            (obj.transform as RectTransform).offsetMax = Vector3.zero;
            (obj.transform as RectTransform).offsetMin = Vector3.zero;
            //获取面板的脚本
            T panel = obj.GetComponent<T>();
            //下边其实就是说 , 如果传入的参数里有第三个参数的话 , 那我就执行下边这段代码 , 第三个参数是个委托 , 就是因为这个showpanel是用异步加载实现的 , 所以不能确定什么时候加载完 , 
            //想要在加载完成后执行某个函数的话就必须用这样的方法 , 先在子类定义一个方法 , 然后把方法的委托传进来 , 下边代码检测到callback不为空就会执行 , 顺便这个函数必须是无返回值有参数的 , 看样子参数还必须是panel脚本
            //简单的说:面板完成后执行下边的方法 , 方法由外部传入
           
            //字典里存每个界面的脚本 , 不要存他们的物体对象 , 到时候要删除物体对象的话就通过脚本.gameObject的方式删 
            panelDic.Add(PanelName, panel);
        });


    }
    public void HidePanel(string PanelName)
    {
        if(panelDic.ContainsKey(PanelName))
        {
            GameObject.Destroy(panelDic[PanelName].gameObject);//在场景中删除这个脚本对应的对象
            panelDic.Remove(PanelName);//从字典中移除这个Panel对应的脚本
        }
    }
    //用来获取一个已经存在的面板 , 然后再进行操作
    public T GetPanel<T>(string panelName) where T:BasePanel
    {
        if (panelDic.ContainsKey(panelName))
            return panelDic[panelName] as T;
        else
            return null;
    }

    public virtual void Show()
    {

    }
    public virtual void Hide()
    {

    }
}
